Step 33: ApiError
We should be doing input validation and error handling in Bookmark DAO as well as in the bookmarks
route handler functions. To that aim, we will create a new class. Add ApiErro.js
to the src/model
folder:
class ApiError extends Error {
constructor(status, message) {
super(message);
this.status = status;
}
}
export default ApiError;
The status
attribute will store the HTTP status number.
In APIs that conform to HTTP, the error status is a "code" (number) returned to the client that signals the success/failure of their request. Here are the common status codes and their meaning:
Status | Meaning |
---|---|
200 (OK) | This is the standard response for successful HTTP requests. |
201 (CREATED) | This is the standard response for an HTTP request that resulted in an item being successfully created. |
204 (NO CONTENT) | This is the standard response for successful HTTP requests, where nothing is being returned in the response body. |
400 (BAD REQUEST) | The request cannot be processed because of bad request syntax, excessive size, or another client error. |
403 (FORBIDDEN) | The client does not have permission to access this resource. |
404 (NOT FOUND) | The resource could not be found at this time. It is possible it was deleted, or does not exist yet. |
500 (INTERNAL SERVER ERROR) | The generic answer for an unexpected failure if there is no more specific information available. |
Let’s update the create
method in the src/data/BookmarkDAO.js
file:
// return the created bookmark
// throws ApiError when title or url are invalid
async create({ title, url }) {
try {
const bookmark = await Bookmark.create({title, url});
return bookmark;
} catch(err) {
throw new ApiError(400, err.message);
}
}
Add these tests to tests/data/BookmarkDAO.test.js
file:
describe("test create() throws error", () => {
it("empty title", async () => {
try {
const title = "";
const url = faker.internet.url();
await bookmarkDAO.create({ title, url });
} catch (err) {
expect(err.status).toBe(400);
}
});
it("null title", async () => {
try {
const title = null;
const url = faker.internet.url();
await bookmarkDAO.create({ title, url });
} catch (err) {
expect(err.status).toBe(400);
}
});
it("undefined title", async () => {
try {
const title = undefined;
const url = faker.internet.url();
await bookmarkDAO.create({ title, url });
} catch (err) {
expect(err.status).toBe(400);
}
});
it("empty url", async () => {
try {
const title = faker.lorem.sentence();
const url = "";
await bookmarkDAO.create({ title, url });
} catch (err) {
expect(err.status).toBe(400);
}
});
it("null url", async () => {
try {
const title = faker.lorem.sentence();
const url = null;
await bookmarkDAO.create({ title, url });
} catch (err) {
expect(err.status).toBe(400);
}
});
it("undefined url", async () => {
try {
const title = faker.lorem.sentence();
const url = undefined;
await bookmarkDAO.create({ title, url });
} catch (err) {
expect(err.status).toBe(400);
}
});
it("invalid url", async () => {
try {
const title = faker.lorem.sentence();
const url = faker.lorem.sentence();
await bookmarkDAO.create({ title, url });
} catch (err) {
expect(err.status).toBe(400);
}
});
});
Run the tests and make sure they all pass. Then, save and commit the changes.